#include "gobject.h"
#include "XParser.h"
gobject::gobject()
{
}

gobject::~gobject()
{
	pMeshRoot -> DeleteMeshCon();
	pFrameRoot -> DeleteFrame();
}

const int TriPointCount = 3;

void gobject::Load(const char *FileName, IDirect3DDevice9 *pDevice, const char *TexturePath)
{
	Parser::Parse(FileName, pDevice, TexturePath);

	D3DXMESHCONTAINER_EX *pMeshCon = Parser::m_RootMesh;
	while(pMeshCon)
	{
		if(pMeshCon->pSkinInfo)
		{
			DWORD NumBones = pMeshCon->pSkinInfo->GetNumBones();

			pMeshCon->ppFrameMatrices = new D3DXMATRIX*[NumBones];
			pMeshCon->pBoneMatrices = new D3DXMATRIX[NumBones];
			pMeshCon->ppBoneFrame = new D3DXFRAME_EX*[NumBones];

			for(DWORD i=0;i<NumBones;i++) 
			{
				const char *BoneName = pMeshCon->pSkinInfo->GetBoneName(i);
				D3DXFRAME_EX *pFrame = Parser::m_RootFrame->Find(BoneName);

				if(pFrame)
				{
					pMeshCon -> ppBoneFrame[i] = pFrame;
					pMeshCon -> ppFrameMatrices[i] = &(pFrame -> matCombined);
				}
				else
				{
					dprintf("find bone error \n");
					pMeshCon -> ppBoneFrame[i] = NULL;
					pMeshCon->ppFrameMatrices[i] = NULL;
				}
			}
		}
		pMeshCon = pMeshCon->pNextMeshContainer;
	} 

	pMeshRoot = Parser::m_RootMesh;

	pFrameRoot = Parser::m_RootFrame;

	return;
}

void PointMultiMatrix(struct Point3DDef *pPoint, D3DXMATRIX *pMatrix)
{
	float x = pPoint -> x;
	float y = pPoint -> y;
	float z = pPoint -> z;
	float t = 1.0;

	pPoint -> x = x * (pMatrix -> m[0][0]) + y * (pMatrix -> m[1][0]) + z * (pMatrix -> m[2][0]) + t * (pMatrix -> m[3][0]);
	pPoint -> y = x * (pMatrix -> m[0][1]) + y * (pMatrix -> m[1][1]) + z * (pMatrix -> m[2][1]) + t * (pMatrix -> m[3][1]);
	pPoint -> z = x * (pMatrix -> m[0][2]) + y * (pMatrix -> m[1][2]) + z * (pMatrix -> m[2][2]) + t * (pMatrix -> m[3][2]);
}

void TriangleMultiMatrix(struct Point3DDef *TrianglePoints, D3DXMATRIX *pMatrix)
{
	for (int i = 0; i < TriPointCount; i ++)
	{
		PointMultiMatrix(&TrianglePoints[i], pMatrix);
	}
}

float CalAngleByTan(float UpValue, float DownValue)
{
	float Angle = 0.0;
	if (DownValue == 0)
	{
		if (UpValue == 0)
		{
			Angle = 0.0;
		}
		else if (UpValue > 0)
		{
			Angle = D3DX_PI / 2;
		}
		else if (UpValue < 0)
		{
			Angle = (D3DX_PI / 2 + D3DX_PI);
		}
	}
	else
	{
		if (UpValue == 0)
		{
			if (DownValue > 0)
			{
				Angle = 0.0;
			}
			else if (DownValue < 0)
			{
				Angle = D3DX_PI;
			}
		}
		else
		{
			float TanValue = (UpValue / DownValue);
			if (TanValue > 0)
			{
				if (DownValue > 0)
				{
					Angle = atan(TanValue);
				}
				else if (DownValue < 0)
				{
					Angle = (atan(TanValue) + D3DX_PI);
				}
			}
			else if (TanValue < 0)
			{
				if (DownValue > 0)
				{
					Angle = 2 * D3DX_PI + atan(TanValue);
				}
				else if (DownValue < 0)
				{
					Angle = D3DX_PI + atan(TanValue);
				}
			}
		}
	}

	return Angle;
}

D3DXMATRIX gobject::OriginPosTrans(struct Point3DDef *SortGJPoints)
{
	int BackZeroIndex = 0;
	D3DXMATRIX ZeroTrans;
	D3DXMatrixTranslation(&ZeroTrans, -(SortGJPoints[BackZeroIndex].x), -(SortGJPoints[BackZeroIndex].y), -(SortGJPoints[BackZeroIndex].z));
	TriangleMultiMatrix(SortGJPoints, &ZeroTrans);

	struct Point3DDef YZeroPoint = SortGJPoints[1];
	YZeroPoint.y = 0.0;

	float Angle = CalAngleByTan(YZeroPoint.z, YZeroPoint.x);

	D3DXMATRIX RotateY;
	D3DXMatrixRotationY(&RotateY, Angle);
	TriangleMultiMatrix(SortGJPoints, &RotateY);

	struct Point3DDef ZZeroPoint = SortGJPoints[1];
	Angle = - CalAngleByTan(ZZeroPoint.y, ZZeroPoint.x);
	D3DXMATRIX RotateZ;
	D3DXMatrixRotationZ(&RotateZ, Angle);
	TriangleMultiMatrix(SortGJPoints, &RotateZ);

	struct Point3DDef XZeroPoint = SortGJPoints[2];
	Angle = - CalAngleByTan(XZeroPoint.z, XZeroPoint.y);
	D3DXMATRIX RotateX;
	D3DXMatrixRotationX(&RotateX, Angle);

	return ZeroTrans * RotateY * RotateZ * RotateX;
}

float CalLen(struct Point3DDef *p1, struct Point3DDef *p2)
{
	float x = p1 -> x - p2 -> x;
	float y = p1 -> y - p2 -> y;
	float z = p1 -> z - p2 -> z;

	return sqrt(x * x +y * y + z * z);
}

void SortAngle(struct Point3DDef *TrianglePoints)
{
	float MaxLen = 0;
	int MaxStart = 0;

	float MinLen = 0;
	int MinStart = 0;

	for (int i = 0; i < TriPointCount; i ++)
	{
		int start_index = i;
		int end_index = i + 1;
		if (end_index >= TriPointCount)
		{
			end_index = 0;
		}

		float TempLen = CalLen(&TrianglePoints[start_index], &TrianglePoints[end_index]);
		if (!MaxLen || (TempLen > MaxLen))
		{
			MaxLen = TempLen;
			MaxStart = start_index;
		}

		if (!MinLen || (TempLen < MinLen))
		{
			MinLen = TempLen;
			MinStart = start_index;
		}
	}

	int MaxAngleIndex = MaxStart - 1;
	if (MaxAngleIndex < 0)
	{
		MaxAngleIndex = 2;
	}
	struct Point3DDef MaxValue = TrianglePoints[MaxAngleIndex];

	int MinAngleIndex = MinStart - 1;
	if (MinAngleIndex < 0)
	{
		MinAngleIndex = 2;
	}
	struct Point3DDef MinValue = TrianglePoints[MinAngleIndex];

	int MiddleAngleIndex = TriPointCount - MaxAngleIndex - MinAngleIndex;
	struct Point3DDef MiddleValue = TrianglePoints[MiddleAngleIndex];

	TrianglePoints[0] = MinValue;
	TrianglePoints[1] = MiddleValue;
	TrianglePoints[2] = MaxValue;

	return;
}

void gobject::GenSortPoints(ID3DXMesh *pMesh, struct Point3DDef *GJPoints)
{
	LPDIRECT3DVERTEXBUFFER9 pVertexBuffer;

	pMesh -> GetVertexBuffer(&pVertexBuffer);
	DWORD NumVertex = pMesh -> GetNumVertices();

	struct VertexDef *pVertex;

	pVertexBuffer -> Lock(0, 0, (void **)(&pVertex), D3DLOCK_DISCARD);

	for (DWORD i = 0; i < NumVertex; i ++)
	{
		GJPoints[i].x = pVertex[i].x;
		GJPoints[i].y = pVertex[i].y;
		GJPoints[i].z = pVertex[i].z;
	}

	struct Point3DDef t1 = GJPoints[0];
	struct Point3DDef t2 = GJPoints[1];
	struct Point3DDef t3 = GJPoints[2];

	pVertexBuffer -> Unlock();

	SortAngle(GJPoints);
}

void gobject::DrawMesh(D3DXMESHCONTAINER_EX *pMeshCon, IDirect3DDevice9 *pDevice)
{
	ID3DXMesh *pDrawMesh = (pMeshCon -> pSkinMesh)?(pMeshCon -> pSkinMesh):(pMeshCon->pMesh);

	for(DWORD i = 0; i < (pMeshCon -> NumMaterials); i++) 
	{
		pDevice -> SetTexture(0, (pMeshCon -> ppTextures[i]));
		pDevice -> SetMaterial(&(pMeshCon -> pMaterials[i].MatD3D));
		pDrawMesh -> DrawSubset(i);
	}
}

void gobject::Draw(IDirect3DDevice9 *pDevice)
{
	D3DXMESHCONTAINER_EX *pMeshCon = pMeshRoot;
	while (pMeshCon) 
	{
		if (pMeshCon -> IsShow)
		{
			DrawMesh(pMeshCon, pDevice);
		}
		pMeshCon = pMeshCon->pNextMeshContainer;
	}
}

